package be.kdg.netpresent;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.*;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;
import java.util.Vector;

public class NetPresentImpl extends UnicastRemoteObject implements
		Serializable, NetPresentInt {
	
	// Default serial ID
	private static final long serialVersionUID = 1L;
	
	// Working set of all presentations in the system as Vector.
	// These are individually stored in a backup and attempted to be
	// restored on server-startup.
	private Vector<Presentation> vPresentations;
	
	// A TreeMap of all the presentations in the system is preserved.
	// All individual presentation names are mapped to a backup name
	// to make the saving/restoring process more uniform.
	private TreeMap< String, String > tsBackUps;
	
	// Help-ID for the backup-creation process.
	private int iHighestID = 0;
	

	// Constructor of the actual service implementation.
	public NetPresentImpl() throws RemoteException {
		
		// Call the super-class constructor
		super();
		
		// Create/initialize the working set of presentations
		vPresentations = new Vector<Presentation>();
		
		// Create/initialize the TreeMap for backup management.
		this.tsBackUps = new TreeMap< String, String >();
		
		// Try to load/read previously backed up presentations.
		try
		{
			// In the first step, read the Presentations.lst. This contains info
			// on how many presentations, and with what name they are stored.
			FileInputStream fisInst1, fisInst0 = new FileInputStream("Presentations.lst");
			ObjectInputStream oisInst1, oisInst0 = new ObjectInputStream(fisInst0);
			
			// The first entry of Presentations.lst is an Integer. This is needed to
			// be able to read the exact amount of presentation names. (Needed due to
			// the artifact that streams cannot work with EOF as a delimiter.)
			int iNumOfBackUps = oisInst0.readInt();
			System.out.println( "#read: " + iNumOfBackUps );
			
			String sTemp0 = new String();
			
			// Now loop for iNumOfBackUps-#-times and read the backup name,
			// and with it the backup itself and store both accordingly in
			// resp. the Vector and the TreeMap.
			for (int iCtr = 0; iCtr < iNumOfBackUps; iCtr++)
			{
				sTemp0 = (String) oisInst0.readObject();
				
				fisInst1 = new FileInputStream(sTemp0);
				oisInst1 = new ObjectInputStream(fisInst1);
				
				Presentation pBackedUp = (Presentation) oisInst1.readObject();
				if( pBackedUp.getID() > iHighestID) iHighestID = pBackedUp.getID();
				tsBackUps.put(sTemp0, pBackedUp.getPresName());
				vPresentations.add(pBackedUp);
				
				oisInst1.close();
			}
			
			oisInst0.close();
		}
		catch (FileNotFoundException e)
		{
			// This exceptions is caused if Presentations.lst is not found.
			// This is an error, but does not really prevent proper usage of
			// the tool, so appropriate messages are printed and the user can
			// proceed to make new presentations. 
			System.out.println("Presentations.lst not found.");
			System.out.println("Assuming that no previous backups were made!");
			e.printStackTrace();
		}
		catch (IOException e)
		{
			// This exception is caused by errors reading from Presentions.lst or
			// when a read-attempt for a backup goes wrong. Both are annoying but
			// again proper usage of the tool can still be guaranteed.
			System.out.println("Error accessing Presentations.lst.");
			System.out.println("Assuming that no previous backups were made!");
			e.printStackTrace();
		}
		catch (ClassNotFoundException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// Method for creating new presentations on the system. A user defined name
	// for the presentation and a valid password should be provided. These variable
	// will be collected through the use of the GUI.
	public PresentationInt makePresentation( String sNewPresName, String sNewPWD )
			throws java.rmi.RemoteException {
		
		// Create a new presentation instance.
		Presentation p = new Presentation(++iHighestID, sNewPresName, sNewPWD );
		
		// Add it to the "working set"-vector. 
		vPresentations.add(p);
		
		// Create a uniform backup name to ensure proper sorting in the TreeMap.
		String sLeadingZeros = new String();
		int iID = p.getID();
		
		if (10 > iID) {
			sLeadingZeros = "000";
		} else if ((10 <= iID) && (100 > iID)) {
			sLeadingZeros = "00";
		} else if ((100 <= iID) && (1000 > iID)) {
			sLeadingZeros = "0";
		}
		
		String sBackUpList = new String("Presentations.lst");
		String sBackUpName = new String("Presentation_" + sLeadingZeros + iID + ".pres");
		
		// "Just" put this key and value into the TreeMap, this is
		// possible due to the fact that if a key is already present,
		// the linked value will automatically be updated.
		this.tsBackUps.put(sBackUpName, sNewPresName);	
		
		// After each new presentation created, overwrite the Presentations.lst
		// with the complete and updated version of the TreeMap.
		FileOutputStream fosInst1;
		ObjectOutputStream oosInst1;
			
		try {
			fosInst1 = new FileOutputStream(sBackUpList);
			oosInst1 = new ObjectOutputStream(fosInst1);
			
			// Keys can only be returned as a set.
			Set< String > tsTemp0 = this.tsBackUps.keySet();
			Iterator<String> isInst0 = tsTemp0.iterator();
			
			// Get size of the TreeMap and store the value indicating the
			// number of backups present in the system. This is needed with
			// the use of stream as there are no other means to check EOF.
			int iNumOfBackUps = tsBackUps.size();
			oosInst1.writeInt(iNumOfBackUps);
			
			// Then write all the filenames in the TreeSet.
			while (isInst0.hasNext())
			{
				oosInst1.writeObject(isInst0.next());
			}
			
			oosInst1.close();
		}
		catch (FileNotFoundException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return (PresentationInt) p;
	}

	// Try to get a presentation by it's ID and a provided password.
	// The presentation, if present by it's ID will only be returned
	// if the correct password is provided.
	public PresentationInt getPresentation( int pNummer, String pPWD )
			throws java.rmi.RemoteException {
		
		Presentation p = vPresentations.get(pNummer);
		
		if (p.open(pPWD)) {
			return (PresentationInt) p;
		} else {
			return null;
		}
	}

	// Method to get a list of user names of all the presentations.
	// This method is used by the GUI to build a list the user can choose
	// form to start a desired presentation.
	public String[] getPresentationList() throws java.rmi.RemoteException
	{
		// Create a String-array with the size of the TreeMap.
		int iNumOfBackUps = this.tsBackUps.size();
		String[] sPresList = new String[iNumOfBackUps];
		
		// Get the Values of the TreeMap as a collection.
		Collection<String> cInst0 = this.tsBackUps.values();
		Iterator<String> iInst0 = cInst0.iterator();
		
		// Walk through all values with an iterator and store
		// the "values", which are the individual presentation names,
		// into the earlier created String-array.
		for( int iCtr = 0; iCtr < iNumOfBackUps; iCtr++)
		{
			sPresList[iCtr] = iInst0.next();
			System.out.println(sPresList[iCtr]);
		}
		
		// Return it...
		return sPresList;
	}

	// In addition to the above method, this one is needed to get a presentation
	// through its selected "user defined name".
	public int getPresIndexByName(String name) throws java.rmi.RemoteException {
		
		Iterator<Presentation> itPresentation = vPresentations.iterator();
		Presentation pTemp = null;
		int iCtr = 0;
		
		while(itPresentation.hasNext())
		{
			pTemp = itPresentation.next();
			
			if(name.equals(pTemp.getPresName())){
				return (iCtr);
			}
			
			iCtr++;
		}
		
		return -1;
	}
}
